//定义部分的文字块
%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

#include "parse_tree.h"

// external functions from lex
extern int yylex();
extern int yyparse();
extern int yyrestart();
extern FILE * yyin;

// external variables from lexical_analyzer module
extern int lines;
extern char * yytext;
extern int pos_end;
extern int pos_start;

// Global parse tree
parse_tree *gt;

// Error reporting
void yyerror(const char *s);

// Helper functions written for you with love
parse_tree_node *node(const char *node_name, int children_num, ...);
%}

/* TODO: Complete this definition.
   Hint: See new_node(), node(), and parse_tree.h.
         Use forward declaring. */
%union {
      struct _parse_tree_node* node;
};

/* TODO: Your tokens here. */

//终结符
%token <node> INT
%token <node> FLOAT 
%token <node> VOID
%token <node> IDENT INTCONST FLOATCONST
%token <node> SEMICOLON COMMA ASSIGN
%token <node> LT LET GT GET EQ NEQ
%token <node> ADD SUB MUL DIV MOD
%token <node> AND OR NOT
%token <node> LPARENTHESIS RPARENTHESIS
%token <node> LBRACKET RBRACKET
%token <node> LBRACE RBRACE
%token <node> RETURN IF ELSE WHILE BREAK CONTINUE CONST

// 非终结符
%type <node> Program
%type <node> CompUnit Decl ConstDecl BType ConstDef ConstInitVal
%type <node> VarDecl VarDef VarDeclList InitVal FuncDef FuncFParams FuncFParam
%type <node> Block BlockItem BlockList Stmt Exp Cond LVal PrimaryExp Number Integer Floatnum
%type <node> UnaryExp UnaryOp FuncRParams MulExp AddExp RelExp EqExp
%type <node> LAndExp LOrExp ConstExp
%type <node> ConstExpList ConstInitValList InitValList ConstDefList ExpList
// 开始符
%start Program


%%
Program: CompUnit { 
        $$ = node("Program", 1, $1); 
        gt->root = $$;}
       ;

// CompUnit规则
CompUnit: CompUnit Decl { $$ = node("CompUnit", 2, $1, $2); }
        | CompUnit FuncDef { $$ = node("CompUnit", 2, $1, $2); }
        | Decl { $$ = node("CompUnit", 1, $1); }
        | FuncDef { $$ = node("CompUnit", 1, $1); }
        ;

// Decl规则
Decl: ConstDecl { $$ = node("Decl", 1, $1); }
    | VarDecl { $$ = node("Decl", 1, $1); }
    ;

// ConstDecl规则
ConstDecl: CONST BType ConstDefList SEMICOLON { $$ = node("ConstDecl", 4, $1, $2, $3, $4); }
         ;

// ConstDefList规则
ConstDefList: ConstDefList COMMA ConstDef { $$ = node("ConstDefList", 3, $1, $2, $3); }
            | ConstDef { $$ = node("ConstDefList", 1, $1); }
            ;

// BType规则
BType: INT { $$ = node("BType", 1, $1); }
     | FLOAT { $$ = node("BType", 1, $1); }
     ;

// ConstDef规则
ConstDef: IDENT ConstExpList ASSIGN ConstInitVal { $$ = node("ConstDef", 4, $1, $2, $3, $4); }

// ConstExpList规则
ConstExpList: ConstExpList LBRACKET ConstExp RBRACKET { $$ = node("ConstExpList", 4, $1, $2, $3, $4); }
            | /* empty */ { $$ = node("ConstExpList", 0); }
            ;

// ConstInitVal规则
ConstInitVal: ConstExp { $$ = node("ConstInitVal", 1, $1); }
            | LBRACE ConstInitValList RBRACE { $$ = node("ConstInitVal", 3, $1, $2, $3); }
            | LBRACE RBRACE { $$ = node("ConstInitVal", 2, $1, $2); }
            ;

// ConstInitValList规则
ConstInitValList: ConstInitValList COMMA ConstInitVal { $$ = node("ConstInitValList", 3, $1, $2, $3); }
                | ConstInitVal { $$ = node("ConstInitValList", 1, $1); }
                ;

// VarDecl规则
VarDecl: BType VarDef VarDeclList SEMICOLON { $$ = node("VarDecl", 4, $1, $2, $3, $4); }
       ;

// VarDeclList规则
VarDeclList: VarDeclList COMMA VarDef { $$ = node("VarDeclList", 3, $1, $2, $3); }
          | /* empty */ { $$ = node("VarDeclList", 0); }
          ;

// VarDef规则
VarDef: IDENT ConstExpList { $$ = node("VarDef", 2, $1, $2); }
      | IDENT ConstExpList ASSIGN InitVal { $$ = node("VarDef", 4, $1, $2, $3, $4); }
      ;

// InitVal规则
InitVal: Exp { $$ = node("InitVal", 1, $1); }
       | LBRACE RBRACE { $$ = node("InitVal", 2, $1, $2); }
       | LBRACE InitValList RBRACE { $$ = node("InitVal", 3, $1, $2, $3); }
       ;

// InitValList规则
InitValList: InitValList COMMA InitVal { $$ = node("InitValList", 3, $1, $2, $3); }
           | InitVal { $$ = node("InitValList", 1, $1); }
           ;

// FuncDef规则
FuncDef: BType IDENT LPARENTHESIS RPARENTHESIS Block { $$ = node("FuncDef", 5, $1, $2, $3, $4, $5); }
      | VOID IDENT LPARENTHESIS RPARENTHESIS Block { $$ = node("FuncDef", 5, $1, $2, $3, $4, $5); }
      | VOID IDENT LPARENTHESIS FuncFParams RPARENTHESIS Block { $$ = node("FuncDef", 6, $1, $2, $3, $4, $5, $6); }
      | BType IDENT LPARENTHESIS FuncFParams RPARENTHESIS Block { $$ = node("FuncDef", 6, $1, $2, $3, $4, $5, $6); }
       ;

// FuncFParams规则
FuncFParams: FuncFParam { $$ = node("FuncFParams", 1, $1); }
           | FuncFParams COMMA FuncFParam{ $$ = node("FuncFParams", 3, $1, $2, $3); }
        ;

// FuncFParam规则
FuncFParam: BType IDENT { $$ = node("FuncFParam", 2, $1, $2); }
          | BType IDENT LBRACKET RBRACKET ExpList { $$ = node("FuncFParam", 5, $1, $2, $3, $4, $5); }
          ;

// ExpList规则
ExpList: ExpList LBRACKET Exp RBRACKET { $$ = node("ExpList", 4, $1, $2, $3, $4); }
       | /* empty */ { $$ = node("ExpList", 0); }
       ;

// Block规则
Block: LBRACE BlockList RBRACE { $$ = node("Block", 3, $1, $2, $3); }
     ;

// BlockList规则
BlockList: BlockList BlockItem { $$ = node("BlockList", 2, $1, $2); }
          | /* empty */ { $$ = node("BlockList", 0); }
        ;

// BlockItem规则
BlockItem: Decl { $$ = node("BlockItem", 1, $1); }
         | Stmt { $$ = node("BlockItem", 1, $1); }
         ;

// Stmt规则
Stmt: LVal ASSIGN Exp SEMICOLON { $$ = node("Stmt", 4, $1, $2, $3, $4); }
    | Exp SEMICOLON { $$ = node("Stmt", 2, $1, $2); }
    | SEMICOLON { $$ = node("Stmt", 1, $1); }
    | Block { $$ = node("Stmt", 1, $1); }
    | IF LPARENTHESIS Cond RPARENTHESIS Stmt ELSE Stmt { $$ = node("Stmt", 7, $1, $2, $3, $4, $5, $6, $7); }
    | IF LPARENTHESIS Cond RPARENTHESIS Stmt { $$ = node("Stmt", 5, $1, $2, $3, $4, $5); }
    | WHILE LPARENTHESIS Cond RPARENTHESIS Stmt { $$ = node("Stmt", 5, $1, $2, $3, $4, $5); }
    | BREAK SEMICOLON { $$ = node("Stmt", 2, $1, $2); }
    | CONTINUE SEMICOLON { $$ = node("Stmt", 2, $1, $2); }
    | RETURN Exp SEMICOLON { $$ = node("Stmt", 3, $1, $2, $3); }
    | RETURN SEMICOLON { $$ = node("Stmt", 2, $1, $2); }
    ;

// Exp规则
Exp: AddExp { $$ = node("Exp", 1, $1); }
   ;

// Cond规则
Cond: LOrExp { $$ = node("Cond", 1, $1); }
    ;

// LVal规则
LVal: IDENT ExpList { $$ = node("LVal", 2, $1, $2); }
      ;

// PrimaryExp规则
PrimaryExp: LPARENTHESIS Exp RPARENTHESIS { $$ = node("PrimaryExp", 3, $1, $2, $3); }
          | LVal { $$ = node("PrimaryExp", 1, $1); }
          | Number { $$ = node("PrimaryExp", 1, $1); }
          ;

// Number规则
Number: Integer { $$ = node("Number", 1, $1); }
      | Floatnum { $$ = node("Number", 1, $1); }
      ;

// UnaryExp规则
UnaryExp: PrimaryExp { $$ = node("UnaryExp", 1, $1); }
        | IDENT LPARENTHESIS FuncRParams RPARENTHESIS { $$ = node("UnaryExp", 4, $1, $2, $3, $4); }
        | IDENT LPARENTHESIS RPARENTHESIS { $$ = node("UnaryExp", 3, $1, $2, $3); }
        | UnaryOp UnaryExp { $$ = node("UnaryExp", 2, $1, $2); }
        ;

// UnaryOp规则
UnaryOp: ADD { $$ = node("UnaryOp", 1, $1); }
       | SUB { $$ = node("UnaryOp", 1, $1); }
       | NOT { $$ = node("UnaryOp", 1, $1); }
       ;

// FuncRParams规则
FuncRParams: Exp { $$ = node("FuncRParams", 1, $1); }
           | FuncRParams COMMA Exp { $$ = node("FuncRParams", 3, $1, $2, $3); }
           ;

// MulExp规则
MulExp: UnaryExp { $$ = node("MulExp", 1, $1); }
      | MulExp MUL UnaryExp { $$ = node("MulExp", 3, $1, $2, $3); }
      | MulExp DIV UnaryExp { $$ = node("MulExp", 3, $1, $2, $3); }
      | MulExp MOD UnaryExp { $$ = node("MulExp", 3, $1, $2, $3); }
      ;

// AddExp规则
AddExp: MulExp { $$ = node("AddExp", 1, $1); }
      | AddExp ADD MulExp { $$ = node("AddExp", 3, $1, $2, $3); }
      | AddExp SUB MulExp { $$ = node("AddExp", 3, $1, $2, $3); }
      ;

// RelExp规则
RelExp: AddExp { $$ = node("RelExp", 1, $1); }
      | RelExp LT AddExp { $$ = node("RelExp", 3, $1, $2, $3); }
      | RelExp GT AddExp { $$ = node("RelExp", 3, $1, $2, $3); }
      | RelExp LET AddExp { $$ = node("RelExp", 3, $1, $2, $3); }
      | RelExp GET AddExp { $$ = node("RelExp", 3, $1, $2, $3); }
      ;

// EqExp规则
EqExp: RelExp { $$ = node("EqExp", 1, $1); }
     | EqExp EQ RelExp { $$ = node("EqExp", 3, $1, $2, $3); }
     | EqExp NEQ RelExp { $$ = node("EqExp", 3, $1, $2, $3); }
     ;

// LAndExp规则
LAndExp: EqExp { $$ = node("LAndExp", 1, $1); }
       | LAndExp AND EqExp { $$ = node("LAndExp", 3, $1, $2, $3); }
       ;

// LOrExp规则
LOrExp: LAndExp { $$ = node("LOrExp", 1, $1); }
      | LOrExp OR LAndExp { $$ = node("LOrExp", 3, $1, $2, $3); }
      ;

// ConstExp规则
ConstExp: AddExp { $$ = node("ConstExp", 1, $1); }
        ;

// Integer规则
Integer: INTCONST{ $$ = node("Integer", 1, $1); }
        ;

// Floatnum规则
Floatnum: FLOATCONST{ $$ = node("Floatnum", 1, $1); }
        ;
%%

/// The error reporting function.
void yyerror(const char * s)
{
    fprintf(stderr, "error at line %d column %d: %s\n", lines, pos_start, s);
    exit(1);
}

/// Parse input from file `input_path`, and prints the parsing results
/// to stdout.  If input_path is NULL, read from stdin.
///
/// This function initializes essential states before running yyparse().
parse_tree *parse(const char *input_path)
{
    if (input_path != NULL) {
        if (!(yyin = fopen(input_path, "r"))) {
            fprintf(stderr, "[ERR] Open input file %s failed.\n", input_path);
            exit(1);
        }
    } else {
        yyin = stdin;
    }
    lines = pos_start = pos_end = 1;
    gt = new_parse_tree();
    yyrestart(yyin);
    yyparse();
    return gt;
}

/// A helper function to quickly construct a tree node.
///
/// e.g. $$ = node("program", 1, $1);
parse_tree_node *node(const char *name, int children_num, ...)
{
    parse_tree_node *p = new_parse_tree_node(name);
    parse_tree_node *child;
    if (children_num == 0) {
        child = new_parse_tree_node("epsilon");
        parse_tree_add_child(p, child);
    } else {
        va_list ap;
        va_start(ap, children_num);
        for (int i = 0; i < children_num; ++i) {
            child = va_arg(ap, parse_tree_node *);
            parse_tree_add_child(p, child);
        }
        va_end(ap);
    }
    return p;
}
